/*
 Copyright (C) 2009 QDSS.org
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.qdss.topisv.api.support;

import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 可恢复Api接口调用
 * 
 * @author <a href="mailto:zhaofang123@gmail.com">FANGFANG ZHAO</a>
 * @since 0.1.0, Nov 22, 2009
 * @version $Id: LimitFrequencyHolder.java 59 2011-03-15 14:40:30Z zhaofang123 $
 */
public abstract class LimitFrequencyHolder {
	
	protected static final Log log = LogFactory.getLog(LimitFrequencyHolder.class);
	
	public static final int DEFAULT_MAX_RETRY = 3;
	
	protected boolean isLoggingLimitWarn = true;
	private AtomicBoolean waitForOutOfFrequencyLimit = new AtomicBoolean(false);
	private boolean notifyThreadStarted = false;

	/**
	 * Api 是否在限制中
	 * 
	 * @return
	 */
	public boolean isLimited() {
		return waitForOutOfFrequencyLimit.get();
	}
	
	/**
	 * 设置 Api 限制中
	 */
	public void setLimited() {
		setLimited(true);
	}
	
	/**
	 * 设置 Api 限制
	 * 
	 * @param limited
	 */
	public void setLimited(boolean limited) {
		waitForOutOfFrequencyLimit.set(true);
	}
	
	/**
	 * 等待 Api 限制恢复，如果需要
	 */
	protected void waitIfNeed() throws InterruptedException {
		if (isLimited()) {
			if (isLoggingLimitWarn && log.isWarnEnabled())
				log.warn("waiting for limit(" + this.getClass().getSimpleName() + "@" + this.hashCode() + ") ....");
			
			startNotifyThreadIfNeed();
			synchronized (this) {
				wait();
			}
		}
	}
	
	// 启动恢复通知线程
	synchronized private void startNotifyThreadIfNeed() {
		if (notifyThreadStarted) 
			return;
		notifyThreadStarted = true;
		
		startNotifyThread();
	}

	/**
	 * 启动状态恢复通知线程
	 */
	protected void startNotifyThread() {
		final LimitFrequencyHolder holder = this;
		(new HolderResetNotifyThread(){
			@Override
			protected void doNotify() {
				synchronized (holder) {
					holder.resetState();
					holder.notifyAll();
				}
			}
		}).start();
	}
	
	/**
	 * 状态恢复
	 */
	protected void resetState() {
		waitForOutOfFrequencyLimit.set(false);
		notifyThreadStarted = false;
	}
	
	
	/**
	 * 通知线程
	 */
	protected static class HolderResetNotifyThread extends Thread {
		
		final private long waitMillis;
		protected HolderResetNotifyThread() {
			this(null);
		}
		
		protected HolderResetNotifyThread(Long waitMillis) {
			this.waitMillis = (waitMillis == null) ? 60 * 1000L : waitMillis;
		}

		@Override
		final public void run() {
			long start = System.currentTimeMillis();
			try {
				Thread.sleep(waitMillis);
				
				if (log.isDebugEnabled())
					log.debug("reset notify thread running ....");
				doNotify();
			} catch (InterruptedException ex) {
				doExited(ex, waitMillis - (System.currentTimeMillis() - start));
			}
		}

		protected void doNotify() {
			log.warn("No notify ....");
		}

		protected void doExited(InterruptedException ex, long waitMillis) {
			log.warn("Exception on thread sleep! Ex: " + ex);
			new HolderResetNotifyThread(waitMillis + 100).start();
		}
	}
}
